#ifdef MSVC
	#include "SDL_image.h"
#else
	#include "SDL/SDL_image.h"
#endif

#include "actor.h"

// Картата, в която се казва дали поле е заето или свободно
extern char map[COLS][ROWS];


/*
 * Това е функция, използвана ексклузивно от Actor класовете.
 * За дадени координати на Актьор, връща в коя плочка се намира.
 * Забележка: Нещо ме съмнява тук. Не мога да се отърва от чувствтото,
 * че има дълбоко скрит edge case bug тук някъде...
 * */
Tile Tile::get_by_coordinates(int x, int y)
{
    int _x = x + TILE_SIZE / 2;
    int _y = y + TILE_SIZE / 2;
    return Tile(_x/TILE_SIZE, _y/TILE_SIZE);
}

Actor::Actor(int _x = 100, int _y = 100, Spritesheet * sheet = NULL, int spriteset = PACMAN):
x(_x),y(_y),
dir(NONE),desired_dir(NONE),
last_move_at(SDL_GetTicks()),
pixels_per_move(2),
moves_per_sprite(10),current_frame(0),
current_move(0),sprites(sheet),current_spriteset(spriteset)
{
    //load_sprites(sheet);
}

void Actor::set_spriteset(int new_set)
{
    current_spriteset = new_set;
}

/*
 * Функция, която се вика по веднъж на кадър и се грижи за движението на героя.
 * Тук се осъществява същинското движение и същинската смяна на посока.
 * */
void Actor::move()
{

    unsigned int now = SDL_GetTicks();
    if(now - last_move_at < 1000.0 / moves_per_second)
        //Още не е време за нов ход.
        return;
    last_move_at = now;

    Tile curTile = this->get_tile();
    // Има заявка за смяна на посоката?
    if(desired_dir != dir)
    {
        // Знам знам. Magic Number.
        bool turning_back = (desired_dir + dir == 3);
        // Ако не се движи, или е точно на кръстопът, или завива назад
        if( dir == NONE || (x%TILE_SIZE==0&&y%TILE_SIZE==0) || turning_back)
            // Ако не се отива в задънена улица, смени посоката.
            if(map[curTile.x + direction[desired_dir][0]][curTile.y + direction[desired_dir][1]] != WALL)
                dir = desired_dir;
    }
    int newx = x + direction[dir][0] * pixels_per_move;
    int newy = y + direction[dir][1] * pixels_per_move;
    Tile newTile = Tile::get_by_coordinates(newx, newy);

    // Ако сме на средата на плочка и напред вече няма път.
    if(x % TILE_SIZE == 0 && y % TILE_SIZE == 0)
    {
        if(map[newTile.x + direction[dir][0]][newTile.y + direction[dir][1]] == WALL)
        {
            this->roadblock_callback();
            return;
        }
    }

    x = newx;
    y = newy;
    // Това е груб хак, който в момента симулира телепорта
    if(x < 0)
        x = SCREEN_WIDTH - TILE_SIZE;
    else if(x > SCREEN_WIDTH - TILE_SIZE)
        x = 0;


    // Анимация:
    if(dir != NONE)
        ++current_move;
    if(current_move >= moves_per_sprite)
    {
        current_frame = (current_frame + 1) % ANIMATION_FRAMES;
        current_move = 0;
    }
}

/*
 * Функция за смяна на посоката на героя.
 * Идеята е да се казва в коя посока *Иска* да завие нашият сладък жълт приятел.
 * По този начин, ако това точно в момента не е възможно, той ще продължи да се опитва.
 * Тоест, ще завие на първото валидно кръстовище. Това помага да се управлява по-лесно.
 * */
void Actor::set_direction(const int newDir)
{
    this->desired_dir = newDir;
}

/*
 * Функция, която връща плочката, на която се брои, че стои моделът на героя.
 * Това се използва основно за Collision detection.
 * За намиране на плочката се използва центърът на Пак-ман/Призрак
 * Забележка: Вбъдеще може да се промени размерът на нашия жълт герой
 * Да е различен от TILE_SIZE. Само казвам.
 * */
Tile Actor::get_tile()
{
    return Tile::get_by_coordinates(x,y);
}

void Actor::draw(SDL_Surface * screen)
{
    int _x = x + TILE_SIZE/2 - ACTOR_SIZE/2;
    int _y = y + TILE_SIZE/2 - ACTOR_SIZE/2;
    apply_surface(_x, _y,
        (*sprites)(current_spriteset, dir*ANIMATION_FRAMES + current_frame),
        screen);
}
